package lifeOfAFish.scenegraph;

import javax.media.opengl.*;

/**
 * A light source in the scene
 * This class represents all types of light sources (ie positional, direction, spot),
 * depending on how the argument vectors are set (in the same way OpenGL deals with it). 
 * @author Erik Bonner
 *
 */
public class sgLight extends sgNode {
	
	// the index of the lightsource in the overall scene
	private int m_index;
	
	// light source parameters
	private float[] m_ambient = new float[4];
	private float[] m_diffuse = new float[4];
	private float[] m_specular = new float[4];
	private float[] m_position = new float[4];
	private float[] m_spotDir = new float[3];
	private float[] m_attenuation = new float[3];
	private float m_spotCutoff; 
	private float m_spotExponent;
	private boolean m_enabled = true;
	private float[] m_black = {0.0f, 0.0f, 0.0f, 1.0f};
	private float m_intensity = 1.0f;
	
	/**
	 * "render" the light
	 */
	public void render()
	{
		GL gl = sgGlobals.gl;
		
		if(m_enabled){
			gl.glLightfv(m_index, GL.GL_AMBIENT, multCopyArray(m_ambient, m_intensity), 0);
			gl.glLightfv(m_index, GL.GL_DIFFUSE, multCopyArray(m_diffuse, m_intensity), 0);
			gl.glLightfv(m_index, GL.GL_SPECULAR, multCopyArray(m_specular, m_intensity), 0);
			gl.glLightfv(m_index, GL.GL_POSITION, m_position, 0);
			
			gl.glLightf(m_index, GL.GL_CONSTANT_ATTENUATION, m_attenuation[0]);
			gl.glLightf(m_index, GL.GL_LINEAR_ATTENUATION, m_attenuation[1]);
			gl.glLightf(m_index, GL.GL_QUADRATIC_ATTENUATION, m_attenuation[2]);
			
			gl.glLightf(m_index, GL.GL_SPOT_CUTOFF, m_spotCutoff);
			gl.glLightfv(m_index, GL.GL_SPOT_DIRECTION, m_spotDir, 0);
			gl.glLightf(m_index, GL.GL_SPOT_EXPONENT, m_spotExponent);
		}
		else{
			gl.glLightfv(m_index, GL.GL_AMBIENT, m_black, 0);
			gl.glLightfv(m_index, GL.GL_DIFFUSE, m_black, 0);
			gl.glLightfv(m_index, GL.GL_SPECULAR, m_black, 0);
		}
		
		
	}
	
	/**
	 * enable
	 */
	public void enable(){
		sgGlobals.gl.glEnable(m_index);
		m_enabled = true;
	}
	
	/**
	 * disable
	 */
	public void disable(){
		sgGlobals.gl.glDisable(m_index);
		m_enabled = false;
	}
	
	
	/**
	 *  objects of this class can only be instantiated by a factory in the 
	 * @param index - the global index of the light in the openGL scene. 
	 */
	protected sgLight(int index){
		this.m_index = index;
		
		float[] v = {1.0f, 1.0f, 1.0f, 1.0f};
		copyArray(m_specular, v);
		copyArray(m_diffuse, v);
		copyArray(m_ambient, v);
		
		// default - positional light
		float[] pos = {1.0f, 1.0f, 1.0f, 1.0f};
		copyArray(m_position, pos);
		
		// default attenuation
		float[] att = {1.5f, 0.5f, 0.2f};
		copyArray(m_attenuation, att);
		
		// default spot direction
		float[] spdir = {-1.0f, -1.0f, -1.0f};
		copyArray(m_spotDir, spdir);
		
		// default spot cutoff is 180, i.e, no spot at all
		m_spotCutoff = 180.0f;
		m_spotExponent = 2.0f;
		
		
	}
	
	// free the light-source index when light is destroyed
	protected void finalize() throws Throwable
	{
		sgLightFactory.freeLight(m_index);
	}
	
	// small method for copying arrays
	private void copyArray(float[] dest, float[] src){
		for (int i = 0; i < dest.length; i++)
			dest[i] = src[i];
	}
	
	// multiply array by scalar
	private void multArray(float[] array, float scalar)
	{
		for (int i = 0; i < array.length; i++){
			array[i] *= scalar;
		}
	}
	
	// multiply array by scalar
	private float[] multCopyArray(float[] array, float scalar)
	{
		float [] ret = new float[array.length];
		for (int i = 0; i < array.length; i++){
			ret[i] = array[i]*scalar;
		}
		return ret;
	}

	public void setAmbient(float[] v) {	copyArray(m_ambient, v); }
	public void setDiffuse(float[] v) {	copyArray(m_diffuse, v); }
	public void setSpecular(float[] v) {copyArray(m_specular, v); }
	public void setPosition(float[] v) {copyArray(m_position, v); }
	public void setSpotDir(float[] v) {	copyArray(m_spotDir, v); }
	public void setAttenuation(float[] v) {	copyArray(m_attenuation, v); }
	public void setSpotCutoff(float v) { m_spotCutoff = v; }
	public void setIntensity(float v) { m_intensity = v; }

	public float[] getAmbient() { return m_ambient; }
    public float[] getDiffuse() { return m_diffuse; }
    public float[] getSpecular() { 	return m_specular; }
	public float[] getPosition() { 	return m_position; }
	public float[] getSpotDir() { return m_spotDir; }
	public float[] getAttenuation() { return m_attenuation; }
	public float getSpotCutoff() { return m_spotCutoff; }
	public float getSpotIntensity() { return m_intensity; }
}
